import { fxDebug } from '@/js/utils/src/debug'
import vuex from '@/store'
import { isWeb } from '@/config/env.config'

const _log = fxDebug('fxUtils')
const newDate = function (date?: string | number | undefined): Date {
	if (typeof date === 'string' && ~date.indexOf('-')) {
		date = date.replace(/-/g, '/')
	}
	return date ? new Date(date) : new Date()
}
const getUUID = function () {
	const r = []
	const s = '0123456789abcdef'.split('')
	for (let i = 0; i < 32; i++) {
		r[i] = s[Math.floor(Math.random() * 0x10)]
	}
	return r.join('')
}
const createUUIDInt = function () {
	let id = 0
	return function () {
		return ++id
	}
}
const getUUIDInt = createUUIDInt()
const getTime = function () {
	return new Date().getTime()
}

const getDomain = function () {
	return isWeb() ? (window.location as Location & { href?: string }).href?.match(/:\/\/([\w.\-_]+(?::\d+)?)\//)?.[1] : vuex.getters.domainGetter
}

// 判断undefined/null by shw
const isDef = function <T> (value: T): boolean {
	return value !== undefined && value !== null
}

const compare = function (origin: unknown[], target: unknown[], log = false): boolean {
	if (typeof target !== 'object' || target === null) {
		const val = origin === target
		if (log && !val) {
			_log(`valpre:${origin},valcur:${target}`)
		}
		return val
	}
	if (typeof origin !== 'object') {
		if (log) {
			_log('origin类型错误')
		}
		return false
	}
	if (Object.keys(origin).length !== Object.keys(target).length) {
		if (log) {
			const originKeys = Object.keys(origin)
			const targetKeys = Object.keys(target)
			const originKeysDiff = originKeys.filter(item => {
				return !~targetKeys.indexOf(item)
			})
			const targetKeysDiff = targetKeys.filter(item => {
				return !~originKeys.indexOf(item)
			})
			const keys = Array.from(new Set([...originKeysDiff, ...targetKeysDiff]))
			_log(`key数量不等,keys:${keys.join(',')}`)
		}
		return false
	}
	for (const key of Object.keys(target) as Array<keyof typeof origin>) {
		if (!compare(origin[key] as unknown[], target[key] as unknown[])) {
			if (log) {
				_log(`key:${String(key)},valpre:, ${origin[key]}, ' ,valcur:', ${target[key]}`)
			}
			return false
		}
	}
	return true
}


// 数组比较工具
class CompareArrayUtils {
	compareData: { [key: string]: boolean } = {}
	compareMap: { [key: string]: string[] } = {}
	constructor (arr: string[], fn: Function) {
		this.setData(arr, fn)
	}

	setData (arr: unknown[], fn: Function) {
		const arrObj: { [key: string]: boolean } = {}
		const arrMap: { [key: string]: string[] } = {}
		arr.forEach(item => {
			const key = typeof fn === 'function' ? fn(item) : item
			arrObj[key] = true
			arrMap[key] = item as string[]
		})
		this.compareData = Object.freeze(arrObj)
		this.compareMap = arrMap
	}

	has (val: string | number) {
		return this.compareData[val]
	}

	get () {
		return this.compareData
	}

	getMap () {
		return this.compareMap
	}
}
const getIntersectionArrByMap = function (pre: { [key: string]: boolean }, cur: { [key: string]: boolean }): string[] {
	return Object.keys(cur).filter(item => Object.prototype.hasOwnProperty.call(pre, item))
}
const compareDiff = function (pre: string[], cur: string[], getKeyFn: Function) {
	return typeof getKeyFn === 'function' ? compareDiffByKey(pre, cur, getKeyFn) : compareDiffNoKey(pre, cur)
}
const compareDiffNoKey = function (pre: string[], cur: string[]) {
	const data: { [key: string]: object } = {}
	if (pre.length > cur.length) {
		const diff = pre.length - cur.length
		data.delete = pre.slice(pre.length - diff)
		if (cur.length !== 0) {
			const update: object[] = []
			cur.forEach((item, index: number) => {
				if (!compare(item as unknown as unknown[], pre[index] as unknown as unknown[])) {
					update.push({ pre: pre[index], cur: item })
				}
			})
			if (update.length > 0) {
				data.update = update
			}
		}
	} else if (pre.length < cur.length) {
		const diff = cur.length - pre.length
		data.add = cur.slice(cur.length - diff)
		if (pre.length !== 0) {
			const update: object[] = []
			pre.forEach((item, index: number) => {
				if (!compare(item as unknown as unknown[], cur[index] as unknown as unknown[])) {
					update.push({ pre: item, cur: cur[index] })
				}
			})
			if (update.length > 0) {
				data.update = update
			}
		}
	} else {
		if (pre.length !== 0) {
			const update: object[] = []
			pre.forEach((item, index: number) => {
				if (!compare(item as unknown as unknown[], cur[index] as unknown as unknown[])) {
					update.push({ pre: item, cur: cur[index] })
				}
			})
			if (update.length > 0) {
				data.update = update
			}
		}
	}
	return data
}
const compareDiffByKey = function (pre: string[], cur: string[], getKeyFn: Function) {
	const data = {
		add: [] as unknown,
		delete: [] as unknown,
		update: [] as unknown
	}
	if (pre.length === 0 && cur.length !== 0) {
		data.add = cur
	} else if (pre.length !== 0 && cur.length === 0) {
		data.delete = pre
	} else if (pre.length !== 0 && cur.length !== 0) {
		const preMap = new CompareArrayUtils(pre, getKeyFn)
		const curMap = new CompareArrayUtils(cur, getKeyFn)
		const intersectionArr = getIntersectionArrByMap(preMap.get(), curMap.get())
		const adds = cur.filter(item => !preMap.has(getKeyFn(item)))
		const dels = pre.filter(item => !curMap.has(getKeyFn(item)))
		const modifys: unknown[] = []
		intersectionArr.forEach(key => {
			if (!compare(preMap.getMap()[key], curMap.getMap()[key])) {
				modifys.push([preMap.getMap()[key], curMap.getMap()[key]])
			}
		})
		if (adds.length > 0) {
			data.add = adds
		}
		if (dels.length > 0) {
			data.delete = dels
		}
		if (modifys.length > 0) {
			data.update = modifys
		}
	}
	return data
}
const getObjDeepValue = function <T, K> (obj: string | { [key: string]: string } | K, prop: string = '', defaultValue = ''): T {
	if (~prop.indexOf('.')) {
		return prop.split('.').reduce((pre: string | { [key: string]: string }, cur: string) => {
			return !isDef(pre) ? defaultValue : (pre as { [key: string]: string })[cur]
		}, obj as string) as T
	}
	return isDef((obj as { [key: string]: string })[prop]) ? (obj as { [key: string]: string })[prop] as T : defaultValue as T
}

const setDateTime = function (date = new Date(), formatter?: string | Function) {
	const pushZero = (val: number) => {
		return val < 10 ? '0' + val : val
	}
	const y = date.getFullYear()
	const m = pushZero(date.getMonth() + 1)
	const d = pushZero(date.getDate())
	const myH = pushZero(date.getHours())
	const myM = pushZero(date.getMinutes())
	const myS = pushZero(date.getSeconds())
	if (typeof formatter === 'function') {
		return formatter(y, m, d, myH, myM, myS)
	}
	switch (formatter) {
		case '年月日':
			return `${y}年${m}月${d}日 ${myH}:${myM}:${myS}`
		case '年月日时分':
			return `${y}年${m}月${d}日 ${myH}:${myM}`
		case '时分':
			return `${y}-${m}-${d} ${myH}:${myM}`
		case 'HH:MM:SS':
			return `${myH}:${myM}:${myS}`
		default:
			return `${y}-${m}-${d} ${myH}:${myM}:${myS}`
	}
}

export default {
	newDate,
	getUUID,
	getUUIDInt,
	getDomain,
	getTime,
	isDef,
	compare,
	compareDiff,
	getObjDeepValue,
	setDateTime
}
